package com.lalilu.lmedia.database

import androidx.room.Transaction

/**
 * 可链接对象的接口定义
 */
interface Linkable {
    /**
     * 存储下一个对象的ID，为空则标识无下一个元素
     */
    var nextId: Long?

    /**
     * 获取当前对象的ID
     */
    fun getLinkableId(): Long

    /**
     * 获取当前对象的ID
     */
    fun setLinkableId(id: Long)

    /**
     * 获取可以在数据表内区分多个链表的ID
     */
    fun getClassifyId(): Long? = null
}

/**
 * 针对查询Linkable对象的扩展DAO接口定义
 *
 * 需确保继承并实现下列的几个 abstract 函数
 */
abstract class LinkableDao<I : Linkable> {

    protected abstract fun insert(item: I): Long
    protected abstract fun insertAll(items: List<@JvmSuppressWildcards I>)
    protected abstract fun remove(item: I)
    abstract fun update(item: I)
    abstract fun updateAll(items: List<@JvmSuppressWildcards I>)
    abstract fun getById(id: Long, classifyId: Long? = null): I?
    abstract fun getByNextId(nextId: Long, classifyId: Long? = null): I?

    /**
     * 获取某分类下的最后一位元素(nextId = null)
     *
     * @param classifyId 分类ID
     */
    abstract fun getLastOne(classifyId: Long? = null): I?

    /**
     * 获取数据表内的最后一位元素(主键最大的一位)
     */
    abstract fun getLastOne(): I?

    /**
     * 判断某元素是否已存在数据表内(可选重载)
     */
    open fun checkExist(item: I): Boolean {
        return false
    }

    /**
     * nextId可能异常丢失，故需要获取可信的NextId
     */
    @Transaction
    open fun getReliableNextId(item: I?): Long? {
        item ?: return null
        return item.nextId ?: getById(item.getLinkableId(), item.getClassifyId())?.nextId
    }

    @Transaction
    open fun insertToEnd(item: I) {
        insertNextTo(getLastOne(item.getClassifyId()), item)
    }

    /**
     * 将一整个元素列表存入数据库
     *
     * 特性：忽略已存有的元素、尚不存在的元素有序存入末端
     */
    @Transaction
    open fun insertToEnd(items: List<I>) {
        // 获取表内最后一位的ID，用于手动分配ID
        var lastId = getLastOne()?.getLinkableId() ?: 0

        items.filter { it.getClassifyId() != null && !checkExist(it) }  // 剔除无法分类的item、已存在于数据库的item
            .onEach { it.setLinkableId(++lastId) }                      // 为每一个item分配一个ID
            .groupBy { it.getClassifyId() }                             // 通过分类ID将元素分类
            .forEach { entry ->                                         // 分类处理
                // 为各个元素设置nextId，使其串联起来
                entry.value.link()

                // 通过分类ID，查找数据库内该分类现有的最后一位Item，将构造好的list的头部链接到其nextId处
                getLastOne(entry.key)?.apply {
                    this.nextId = entry.value.firstOrNull()?.getLinkableId()
                    update(this)
                }
                // 将构造好的各个元素存入数据库
                insertAll(entry.value)
            }
    }

    @Transaction
    open fun insertNextTo(before: I?, item: I) {
        item.nextId = getReliableNextId(before)
        val newId = insert(item).let { item.getLinkableId().takeIf { num -> num > 0 } ?: it }

        // 若前一个元素存在，则更新前一个元素的 nextId
        before?.apply {
            nextId = newId
            update(this)
        }
    }

    /**
     * 在链表结构中删除一个Item
     *
     * 核心任务是：
     * 1. 将该元素所持有的 [Linkable.nextId] 填充至前一个元素的 [Linkable.nextId] 处
     * 2. 在数据库中移除该元素
     */
    @Transaction
    open fun removeItem(item: I) {
        // 删除元素会使其他元素的nextId更新，故无法从传入的item中拿到最新的nextId，故需要重新查找数据库
        val nextIdTmp = getById(item.getLinkableId(), item.getClassifyId())?.nextId

        // 删除该元素
        remove(item)

        // 更新前一个元素的nextId
        getByNextId(item.getLinkableId(), item.getClassifyId())?.apply {
            nextId = nextIdTmp
            update(this)
        }
    }

    /**
     * 移动一个元素到目标元素的前或后
     *
     * @param item          所需要移动的元素
     * @param target        目标元素
     * @param beforeTarget  是否移动到目标元素的前面
     */
    @Transaction
    open fun moveItem(item: I, target: I, beforeTarget: Boolean) {
        getByNextId(item.getLinkableId(), item.getClassifyId())?.apply {
            nextId = item.nextId
            update(this)
        }

        if (beforeTarget) {
            getByNextId(target.getLinkableId(), item.getClassifyId())?.apply {
                nextId = item.getLinkableId()
                update(this)
            }
            item.nextId = target.getLinkableId()
            update(item)
        } else {
            item.nextId = target.nextId
            target.nextId = item.getLinkableId()
            update(target)
            update(item)
        }
    }

    @Transaction
    open fun removeItems(item: List<I>) {
        // TODO 待优化流程，需要更高效的方法

        item.forEach {
            removeItem(it)
        }

//        if (item.isEmpty()) return
//        val removeTarget = item.sort()
//        if (removeTarget.isEmpty()) return
//
//        val head = removeTarget.first()
//        val tail = removeTarget.last()
//
//        // 直接删除一串对象
//        removeTarget.forEach { remove(it) }
//
//        // 更新被删除的元素的前一个元素
//        getByNextId(head.getLinkableId())?.apply {
//            nextId = tail.nextId
//            update(this)
//        }
    }
}

/**
 * 根据列表当前的遍历顺序更新 [Linkable#nextId]
 */
fun <I : Linkable> List<I>.link() = onEachIndexed { index, linkable ->
    linkable.nextId = this.getOrNull(index + 1)?.getLinkableId()
}

/**
 * 通过 [Linkable#nextId] 将列表按指定顺序排序
 */
fun <I : Linkable> List<I>.sort(reverse: Boolean = false): List<I> {
    var temp: I? = null
    var index: Int

    return toMutableList().apply {
        while (true) {
            index = indexOfFirst { it.nextId == temp?.getLinkableId() }
            if (index <= -1) break
            temp = removeAt(index)
            add(temp!!)
        }
        if (!reverse) {
            reverse()
        }
    }
}